<template>
  <form class="df-form" :style="formSchema.formStyle" :class="formSchema.formClass" v-on:submit.prevent="submitForm()">
    <template
      v-for="(field, index) in formSchema.fields">
      <div :is="getComponent('FormGroup')"
           :index="index"
           :locale="locale"
           :field="field"
           :schema="formSchema"
           :model="formModel"
           :formModel="formModel"
           :form-submitting="submitting"
           @loading="onLoading"
           @validate="onValidate"
           @pending="onPending"
           @field-destroyed="onFieldDestroyed"></div>
    </template>

    <div class="df-actions" :style="formSchema.actionWrapperStyle" :class="formSchema.actionWrapperClass" v-if="formSchema.actions && formSchema.actions.length > 0">
      <template v-for="action in formSchema.actions">
        <div
          v-if="!action.hidden"
          :is="getComponent('ActionButton')"
          @action="actionHandler"
          :action="action"
          :locale="locale"
          :schema="formSchema"
          :disabled="checkDisable(action)"></div>
      </template>
    </div>

  </form>
</template>

<script>
//  import $ from 'jquery'
  import _ from 'lodash'
  import Vue from 'vue'
  import {getComponent} from './components'
  import LOCALES from './locales'

  export default {
    props: {
      schema: {
        type: Object,
        required: true
      },
      model: {
        type: Object
      }
    },
    data () {
      return {
        submitting: false,
        _originalModel: {},
        formModel: this.model,
        formSchema: {},
        errors: [],
        pendings: [],
        jqModalEle: null,
        jqModalCloseEle: null
      }
    },
    mounted () {
      this.reset()

      // 获取此Form最近的Bootstrap的Modal元素
//      this.jqModalEle = $(this.$el).closest('.modal')
//      if (this.jqModalEle.length > 0) {
//        this.jqModalCloseEle = this.jqModalEle.find('button.close')
//      } else {
//        this.jqModalEle = null
//      }
    },
    computed: {
      locale () {
        let localeString
        if (this.formSchema.locale) {
          localeString = this.formSchema.locale.trim().toUpperCase().replace('-', '_')
          LOCALES.SetLocale(localeString)
        }
        return LOCALES.CurrentLocale
      },
      submitDisabled () {
        return this.errors.length > 0 || this.pendings.length > 0
      },
      resetDisabled () {
        return this.pendings.length > 0
      }
    },
    methods: {
      getComponent: getComponent,
      getModel () {
        return this.formModel
      },
      onLoading (field, isLoading) {
        // TODO
      },
      onValidate (params) {
        if (_.isNil(params.rule)) {
          _.remove(this.errors, item => item.field === params.field && item.model === params.model)
          this.errors = [].concat(this.errors)
        } else {
          if (!_.head(this.errors, item => item.field === params.field && item.model === params.model)) {
            this.errors.push(params)
          }
        }
        console.log(this.errors)
      },
      onPending (params) {
        if (!params.isPending) {
          _.remove(this.pendings, item => item.field === params.field && item.model === params.model)
          this.pendings = [].concat(this.pendings)
        } else {
          if (!_.head(this.pendings, item => item.field === params.field && item.model === params.model)) {
            this.pendings.push(params)
          }
        }
      },
      onFieldDestroyed (params) {
        _.remove(this.pendings, item => item.field === params.field && item.model === params.model)
        _.remove(this.errors, item => item.field === params.field && item.model === params.model)
        this.pendings = [].concat(this.pendings)
        this.errors = [].concat(this.errors)
      },
      setFormSubmittingState (v) {
        this.submitting = v
        this.jqModalCloseEle && this.jqModalCloseEle.prop && this.jqModalCloseEle.prop('disabled', v)
      },
      reset () {
        this.formSchema = this.schema
        if (!this.formSchema.commonLabelClass) {
          this.formSchema.commonLabelClass = 'df-control-label'
        }
        if (!this.formSchema.commonHelpClass) {
          this.formSchema.commonHelpClass = 'df-help-block'
        }
        if (!this.formSchema.commonInputGroupClass) {
          this.formSchema.commonInputGroupClass = 'df-input-group'
        }
        if (!this.formSchema.commonFormControlClass) {
          this.formSchema.commonFormControlClass = 'df-form-control'
        }
        if (!this.formSchema.commonInputGroupAddonClass) {
          this.formSchema.commonInputGroupAddonClass = 'df-input-group-addon'
        }
        if (!this.formSchema.commonButtonClass) {
          this.formSchema.commonButtonClass = null
        }
        if (!this.formSchema.errorFieldClass) {
          this.formSchema.errorFieldClass = 'df-has-error'
        }

        this.errors = []
        this.pendings = []
        this._originalModel = _.cloneDeep(this.model)
        _.forOwn(this.model, (value, key) => {
          _.set(this.model, key, undefined)
        })
        _.assign(this.model, this._originalModel)
        this.formModel = this.model

//        Vue.nextTick(() => {
//          this.validateForm()
//        })
      },
      validateForm () {
        _.forEach(this.$fields, field => {
          field.vm && field.vm.validateSync()
        })
      },
      _closeModal () {
        this.jqModalEle && this.jqModalEle.modal && this.jqModalEle.modal('hide')
      },
      _defaultHandler (action) {
        if (action.type === 'submit') {
          this.submitForm()
        } else if (action.type === 'reset') {
          this.reset()
          this._closeModal()
        } else {
          this._closeModal()
        }
      },
      actionHandler (action) {
        if (action.handler) {
          let rst = action.handler(action, this.formModel)
          if (rst === false) {
            return
          } else if (rst && rst.then && rst.then instanceof Function) {
            if (action.type === 'submit' || action.type === 'reset') {
              this.setFormSubmittingState(true)
            }
            Vue.set(action, 'pendings', true)
            rst.then(data => {
              if (data !== false) {
                this._defaultHandler(action)
              }
            })['finally'](() => {
              if (action.type === 'submit' || action.type === 'reset') {
                this.setFormSubmittingState(false)
              }
              Vue.set(action, 'pendings', false)
            })
            return
          }
        }
        this._defaultHandler(action)
      },
      submitForm () {
        if (this.submitting || this.submitDisabled) {
          return false
        }

        if (this.formSchema.submitHandler) {
          if (this.formSchema.submitHandler instanceof Function) {
            let rst = this.formSchema.submitHandler(this.formModel)
            if (rst === false) {
              return
            } else if (rst.then && rst.then instanceof Function) {
              this.setFormSubmittingState(true)
              let submitAction
              _.forEach(this.formSchema.actions, action => {
                if (action.type === 'submit') {
                  submitAction = action
                  Vue.set(action, 'pendings', true)
                  return false
                }
              })
              rst.then(data => {
                if (data !== false) {
                  this._closeModal()
                }
              })['finally'](() => {
                this.setFormSubmittingState(false)
                submitAction && Vue.set(submitAction, 'pendings', false)
              })
              return
            }
          } else {
            throw new Error('submitHandler must be function...')
          }
        }

        this._closeModal()
      },
      checkDisable (action) {
        return this.submitting || action.disabled || action.pending ||
          (action.type === 'submit' && this.submitDisabled) ||
          (action.type === 'reset' && this.resetDisabled)
      }
    },
    watch: {
      schema (newVal, oldVal) {
        if (newVal !== oldVal) {
          this.reset()
        }
      },
      model (newVal, oldVal) {
        if (newVal !== oldVal) {
          this.reset()
        }
      }
    }
  }
</script>

<style lang="scss">
  @import "css/main";
</style>
